/******************************************************************************
 *
 *  Copyright (C) 2012-2014 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/

#include <string.h>
#include "nfc_hal_int.h"
#include "userial.h"

/*****************************************************************************
* Definitions
*****************************************************************************/

/* Internal flags */
#define NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF  0x01    /* Application provided patchram in a single buffer */
#define NFC_HAL_PRM_FLAGS_RFU               0x02    /* Reserved for future use */
#define NFC_HAL_PRM_FLAGS_SIGNATURE_SENT    0x04    /* Signature sent to NFCC */
#define NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED  0x08    /* PreI2C patch required */
#define NFC_HAL_PRM_FLAGS_BCM20791B3        0x10    /* B3 Patch (no RESET_NTF after patch download) */
#define NFC_HAL_PRM_FLAGS_RM_RF             0x20    /* Erase Personality data */

/* Secure patch download definitions */
#define NFC_HAL_PRM_NCD_PATCHFILE_HDR_LEN  7       /* PRJID + MAJORVER + MINORVER + COUNT */

/* Enumeration of power modes IDs */
#define NFC_HAL_PRM_SPD_POWER_MODE_LPM     0
#define NFC_HAL_PRM_SPD_POWER_MODE_FPM     1

/* Version string for BCM20791B3 */
const UINT8 NFC_HAL_PRM_BCM20791B3_STR[]   = "20791B3";
#define NFC_HAL_PRM_BCM20791B3_STR_LEN     (sizeof (NFC_HAL_PRM_BCM20791B3_STR)-1)

#define NFC_HAL_PRM_SPD_TOUT                   (6000)  /* timeout for SPD events (in ms)   */
#define NFC_HAL_PRM_END_DELAY                  (250)   /* delay before sending any new command (ms)*/

#if (NFC_HAL_PRM_DEBUG == TRUE)
#define NFC_HAL_PRM_STATE(str)  HAL_TRACE_DEBUG2 ("%s st: %d", str, nfc_hal_cb.prm.state)
#else
#define NFC_HAL_PRM_STATE(str)
#endif

void nfc_hal_prm_post_baud_update (tHAL_NFC_STATUS status);
typedef struct
{
    UINT16              offset;
    UINT8               len;
} tNFC_HAL_PRM_RM_RF;

const tNFC_HAL_PRM_RM_RF nfc_hal_prm_rm_rf_20795a1 [] =
{
    {0x0000,    0xFB},
    {0x019C,    0x08},
    {0x05E8,    0xFB},
    {0,         0}
};
static BOOLEAN nfc_hal_prm_nvm_rw_cmd(void);

/*****************************************************************************
** Extern variable from nfc_hal_dm_cfg.c
*****************************************************************************/
extern tNFC_HAL_CFG *p_nfc_hal_cfg;

/*******************************************************************************
**
** Function         nfc_hal_prm_spd_handle_download_complete
**
** Description      Patch download complete (for secure patch download)
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_spd_handle_download_complete (UINT8 event)
{
    nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_IDLE;

    /* Notify application now */
    if (nfc_hal_cb.prm.p_cback)
        (nfc_hal_cb.prm.p_cback) (event);
}

/*******************************************************************************
**
** Function         nfc_hal_prm_spd_send_next_segment
**
** Description      Send next patch segment (for secure patch download)
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_spd_send_next_segment (void)
{
    UINT8   *p_src;
    UINT16  len, offset = nfc_hal_cb.prm.cur_patch_offset;
    UINT8   hcit, oid, hdr0, type;
    UINT8   chipverlen;
    UINT8   chipverstr[NCI_SPD_HEADER_CHIPVER_LEN];
    UINT8   patch_hdr_size = NCI_MSG_HDR_SIZE + 1; /* 1 is for HCIT */

    /* Validate that segment is at least big enought to have NCI_MSG_HDR_SIZE + 1 (hcit) */
    if (nfc_hal_cb.prm.cur_patch_len_remaining < patch_hdr_size)
    {
        HAL_TRACE_ERROR0 ("Unexpected end of patch.");
        nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT);
        return;
    }

    /* Parse NCI command header */
    p_src = (UINT8*) (nfc_hal_cb.prm.p_cur_patch_data + offset);
    STREAM_TO_UINT8 (hcit, p_src);
    STREAM_TO_UINT8 (hdr0, p_src);
    STREAM_TO_UINT8 (oid,  p_src);
    STREAM_TO_UINT8 (len,  p_src);
    STREAM_TO_UINT8 (type, p_src);


    /* Update number of bytes comsumed */
    nfc_hal_cb.prm.cur_patch_offset += (len + patch_hdr_size);
    nfc_hal_cb.prm.cur_patch_len_remaining -=  (len + patch_hdr_size);

    /* Check if sending signature byte */
    if (  (oid == NCI_MSG_SECURE_PATCH_DOWNLOAD )
        &&(type == NCI_SPD_TYPE_SIGNATURE)  )
    {
        nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_SIGNATURE_SENT;
    }
    /* Check for header */
    else if (  (oid == NCI_MSG_SECURE_PATCH_DOWNLOAD )
             &&(type == NCI_SPD_TYPE_HEADER)  )
    {
        /* Check if patch is for BCM20791B3 */
        p_src += NCI_SPD_HEADER_OFFSET_CHIPVERLEN;
        STREAM_TO_UINT8 (chipverlen, p_src);
        if (memcmp (nfc_hal_cb.nvm_cb.chip_ver, p_src, chipverlen) != 0)
        {
            HAL_TRACE_ERROR0 ("Unexpected chip ver.");
            nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT);
            return;
        }
        STREAM_TO_ARRAY (chipverstr, p_src, NCI_SPD_HEADER_CHIPVER_LEN);

        if (memcmp (NFC_HAL_PRM_BCM20791B3_STR, chipverstr, NFC_HAL_PRM_BCM20791B3_STR_LEN) == 0)
        {
            /* Patch is for BCM2079B3 - do not wait for RESET_NTF after patch download */
            nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_BCM20791B3;
        }
        else
        {
            /* Patch is for BCM2079B4 or newer - wait for RESET_NTF after patch download */
            nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_BCM20791B3;
        }
    }

    /* Send the command (not including HCIT here) */
    nfc_hal_dm_send_nci_cmd ((UINT8*) (nfc_hal_cb.prm.p_cur_patch_data + offset + 1), (UINT8) (len + NCI_MSG_HDR_SIZE),
                             nfc_hal_prm_nci_command_complete_cback);
}

/*******************************************************************************
**
** Function         nfc_hal_prm_spd_handle_next_patch_start
**
** Description      Handle start of next patch (for secure patch download)
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_spd_handle_next_patch_start (void)
{
    UINT32  cur_patch_mask;
    UINT32  cur_patch_len;
    BOOLEAN found_patch_to_download = FALSE;

    while (!found_patch_to_download)
    {
        /* Get length of current patch */
        cur_patch_len = nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].len;

        /* Check if this is a patch we need to download */
        cur_patch_mask = ((UINT32) 1 << nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].power_mode);
        if (nfc_hal_cb.prm.spd_patch_needed_mask & cur_patch_mask)
        {
            found_patch_to_download = TRUE;
        }
        else
        {
            /* Do not need to download this patch. Skip to next patch */
            HAL_TRACE_DEBUG1 ("Skipping patch for power_mode %i.", nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].power_mode);

            nfc_hal_cb.prm.spd_cur_patch_idx++;
            if (nfc_hal_cb.prm.spd_cur_patch_idx >= nfc_hal_cb.prm.spd_patch_count)
            {
                /* No more to download */
                nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_COMPLETE_EVT);
                return;
            }
            else if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF))
            {
                /* Notify adaptation layer to call HAL_NfcPrmDownloadContinue with the next patch header */
                (nfc_hal_cb.prm.p_cback) (NFC_HAL_PRM_SPD_GET_NEXT_PATCH);
                return;
            }
            else
            {
                /* Patch in buffer. Skip over current patch. Check next patch */
                nfc_hal_cb.prm.cur_patch_len_remaining -= (UINT16) cur_patch_len;
                nfc_hal_cb.prm.cur_patch_offset += (UINT16) cur_patch_len;
            }
        }
    }


    /* Begin downloading patch */
    HAL_TRACE_DEBUG1 ("Downloading patch for power_mode %i.", nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].power_mode);
    nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_DOWNLOADING;
    nfc_hal_prm_spd_send_next_segment ();
}

#if (defined (NFC_HAL_PRE_I2C_PATCH_INCLUDED) && (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE))
/*******************************************************************************
**
** Function         nfc_hal_prm_spd_download_i2c_fix
**
** Description      Start downloading patch for i2c fix
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_spd_download_i2c_fix (void)
{
    UINT8 *p, *p_start;
    UINT16 patchfile_project_id;
    UINT16 patchfile_ver_major;
    UINT16 patchfile_ver_minor;
    UINT16 patchfile_patchsize;
    UINT8 u8;

    HAL_TRACE_DEBUG0 ("Downloading I2C fix...");

    /* Save pointer and offset of patchfile, so we can resume after downloading the i2c fix */
    nfc_hal_cb.prm.spd_patch_offset = nfc_hal_cb.prm.cur_patch_offset;
    nfc_hal_cb.prm.spd_patch_len_remaining = nfc_hal_cb.prm.cur_patch_len_remaining;

    /* Initialize pointers for downloading i2c fix */
    nfc_hal_cb.prm.p_cur_patch_data = nfc_hal_cb.prm_i2c.p_patch;
    nfc_hal_cb.prm.cur_patch_offset = 0;
    nfc_hal_cb.prm.cur_patch_len_remaining = nfc_hal_cb.prm_i2c.len;

    /* Parse the i2c patchfile */
    if (nfc_hal_cb.prm.cur_patch_len_remaining >= NFC_HAL_PRM_NCD_PATCHFILE_HDR_LEN)
    {
        /* Parse patchfile header */
        p = (UINT8 *) nfc_hal_cb.prm.p_cur_patch_data;
        p_start = p;
        STREAM_TO_UINT16 (patchfile_project_id, p);
        STREAM_TO_UINT16 (patchfile_ver_major, p);
        STREAM_TO_UINT16 (patchfile_ver_minor, p);

        /* RFU */
        p++;

        /* Check how many patches are in the patch file */
        STREAM_TO_UINT8 (u8, p);

        /* Should only be one patch */
        if (u8 > 1)
        {
            HAL_TRACE_ERROR1 ("Invalid i2c fix: invalid number of patches (%i)", u8);
            nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT);
            return;
        }


        /* Get info about the i2c patch*/
        STREAM_TO_UINT8 (u8, p);                     /* power mode (not needed for i2c patch)    */
        STREAM_TO_UINT16 (patchfile_patchsize, p);   /* size of patch                            */

        /* 5 byte RFU */
        p += 5;

        /* Adjust length to exclude patchfiloe header */
        nfc_hal_cb.prm.cur_patch_len_remaining -= (UINT16) (p - p_start);       /* Adjust size of patchfile                        */
        nfc_hal_cb.prm.cur_patch_offset += (UINT16) (p - p_start);              /* Bytes of patchfile transmitted/processed so far */

        /* Begin sending patch to the NFCC */
        nfc_hal_prm_spd_send_next_segment ();
    }
    else
    {
        /* ERROR: Bad length for patchfile */
        HAL_TRACE_ERROR0 ("Invalid i2c fix: unexpected end of patch");
        nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT);
    }
}
#endif /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */

/*******************************************************************************
**
** Function         nfc_hal_prm_spd_check_version_continue
**
** Description      Check patchfile version with current downloaded version
**
** Returns          void
**
*******************************************************************************/
static void nfc_hal_prm_spd_check_version_continue (void)
{
    HAL_TRACE_DEBUG1 ("nfc_hal_prm_spd_check_version_continue 0x%02x", nfc_hal_cb.prm.flags);
    if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_RM_RF)
    {
        HAL_TRACE_DEBUG0("erase relevant blocks in NVM");
        nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_RM_RF;
        if (!nfc_hal_prm_nvm_rw_cmd())
        {
            /* nvm rw started successfully */
            return;
        }
    }
#if (defined (NFC_HAL_PRE_I2C_PATCH_INCLUDED) && (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE))
    if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED)
    {
        HAL_TRACE_DEBUG0 ("I2C patch fix required.");
        /* Download i2c fix first */
        nfc_hal_prm_spd_download_i2c_fix ();
        return;
    }
#endif  /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */

    /* Download first segment */
    nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER;
    if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF))
    {
        /* Notify adaptation layer to call HAL_NfcPrmDownloadContinue with the next patch segment */
        (nfc_hal_cb.prm.p_cback) (NFC_HAL_PRM_SPD_GET_NEXT_PATCH);
    }
    else
    {
        nfc_hal_prm_spd_handle_next_patch_start ();
    }
}

/*******************************************************************************
**
** Function         nfc_hal_prm_spd_check_version
**
** Description      Check patchfile version with current downloaded version
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_spd_check_version (void)
{
    UINT8 *p, *p_start, i;
    UINT32 nvm_patch_present_mask = 0;
    UINT32 patchfile_patch_present_mask;
    UINT16 patchfile_project_id = 0;
    UINT16 patchfile_ver_major = 0;
    UINT16 patchfile_ver_minor = 0;
    UINT16 patchfile_patchsize;

    UINT8  return_code = NFC_HAL_PRM_COMPLETE_EVT;

    /* Initialize patchfile offset pointers */
    p = p_start = NULL;
    patchfile_patchsize = 0;

    /* the good patches in NVM */
    if (nfc_hal_cb.nvm_cb.lpm_size && !(nfc_hal_cb.nvm_cb.flags & (NFC_HAL_NVM_FLAGS_LPM_BAD)))
        nvm_patch_present_mask  |= (1 << NFC_HAL_PRM_SPD_POWER_MODE_LPM);

    if (nfc_hal_cb.nvm_cb.fpm_size && !(nfc_hal_cb.nvm_cb.flags & (NFC_HAL_NVM_FLAGS_FPM_BAD)))
        nvm_patch_present_mask  |= (1 << NFC_HAL_PRM_SPD_POWER_MODE_FPM);

    /* Get patchfile version */
    if (nfc_hal_cb.prm.cur_patch_len_remaining >= NFC_HAL_PRM_NCD_PATCHFILE_HDR_LEN)
    {
        /* Parse patchfile header */
        p       = (UINT8 *) nfc_hal_cb.prm.p_cur_patch_data;
        p_start = p;
        STREAM_TO_UINT16 (patchfile_project_id, p);
        STREAM_TO_UINT16 (patchfile_ver_major, p);
        STREAM_TO_UINT16 (patchfile_ver_minor, p);

        /* RFU */
        p++;

        /* Check how many patches are in the patch file */
        STREAM_TO_UINT8 (nfc_hal_cb.prm.spd_patch_count, p);

        if (nfc_hal_cb.prm.spd_patch_count > NFC_HAL_PRM_MAX_PATCH_COUNT)
        {
            HAL_TRACE_ERROR2 ("Unsupported patchfile (number of patches (%i) exceeds maximum (%i)",
                               nfc_hal_cb.prm.spd_patch_count, NFC_HAL_PRM_MAX_PATCH_COUNT);
        }

        /* Mask of patches that are present in the patchfile */
        patchfile_patch_present_mask = 0;

        /* Get lengths for each patch */
        for (i = 0; i < nfc_hal_cb.prm.spd_patch_count; i++)
        {
            /* Get power mode for this patch */
            STREAM_TO_UINT8 (nfc_hal_cb.prm.spd_patch_desc[i].power_mode, p);

            /* Update mask of power-modes present in the patchfile */
            patchfile_patch_present_mask |= ((UINT32) 1 << nfc_hal_cb.prm.spd_patch_desc[i].power_mode);

            /* Get length of patch */
            STREAM_TO_UINT16 (nfc_hal_cb.prm.spd_patch_desc[i].len, p);

            /* Add total size of patches */
            patchfile_patchsize += nfc_hal_cb.prm.spd_patch_desc[i].len;

            /* 5 byte RFU */
            p += 5;
        }

        /* Adjust offset to after the patch file header */
        nfc_hal_cb.prm.cur_patch_offset += (UINT16) (p - p_start);              /* Bytes of patchfile transmitted/processed so far */
        nfc_hal_cb.prm.cur_patch_len_remaining -= (UINT16) (p - p_start);       /* Adjust size of patchfile                        */


        HAL_TRACE_DEBUG4 ("NVM Patch info: flags=0x%04x,   Ver=%i.%i, PatchMask=0x%08x",
            nfc_hal_cb.nvm_cb.flags, nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor, nvm_patch_present_mask );
        HAL_TRACE_DEBUG6 ("Patchfile info: ProjID=0x%04x,  Ver=%i.%i, Num patches=%i, PatchMask=0x%08x, PatchSize=%i",
                           patchfile_project_id, patchfile_ver_major, patchfile_ver_minor,
                           nfc_hal_cb.prm.spd_patch_count, patchfile_patch_present_mask, patchfile_patchsize);

        /*********************************************************************
        * Version check of patchfile against NVM
        *********************************************************************/
        /* Download the patchfile if no patches in NVM */
        if ((nfc_hal_cb.nvm_cb.project_id == 0) || !(nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_PATCH_PRESENT))
        {
            /* No patch in NVM, need to download all */
            nfc_hal_cb.prm.spd_patch_needed_mask = patchfile_patch_present_mask;
            if (nfc_hal_cb.dev_cb.brcm_hw_id == BRCM_20795A1_ID)
            {
                nfc_hal_cb.prm.flags   |= NFC_HAL_PRM_FLAGS_RM_RF;
                nfc_hal_cb.prm.p_param  = (void *)nfc_hal_prm_rm_rf_20795a1;
                nfc_hal_cb.prm.param_idx = 0;
            }

            HAL_TRACE_DEBUG2 ("No previous patch detected. Downloading patch %i.%i",
                              patchfile_ver_major, patchfile_ver_minor);
        }
        /* Skip download if project ID of patchfile does not match NVM */
        else if (nfc_hal_cb.nvm_cb.project_id != patchfile_project_id)
        {
            /* Project IDs mismatch */
            HAL_TRACE_DEBUG2 ("Patch download skipped: Mismatched Project ID (NVM ProjId: 0x%04x, Patchfile ProjId: 0x%04x)",
                              nfc_hal_cb.nvm_cb.project_id, patchfile_project_id);

            return_code = NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT;
        }
        /* Skip download if version of patchfile is equal to version in NVM */
        /*                  and patches of the power modes are the same as the good patches in NVM */
        else if (  (nfc_hal_cb.nvm_cb.ver_major == patchfile_ver_major)
            &&(nfc_hal_cb.nvm_cb.ver_minor == patchfile_ver_minor)
                 &&((nvm_patch_present_mask | patchfile_patch_present_mask) == nvm_patch_present_mask)  ) /* if the NVM patch include all the patched in file */
        {
            HAL_TRACE_DEBUG2 ("Patch download skipped. NVM patch (version %i.%i) is the same than the patchfile ",
                              nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor);

            return_code = NFC_HAL_PRM_COMPLETE_EVT;
        }
        /* Remaining cases: Download all patches in the patchfile */
        else
        {
            nfc_hal_cb.prm.spd_patch_needed_mask = patchfile_patch_present_mask;

            HAL_TRACE_DEBUG4 ("Downloading patch version: %i.%i (previous version in NVM: %i.%i)...",
                              patchfile_ver_major, patchfile_ver_minor,
                              nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor);
        }

    }
    else
    {
        /* Invalid patch file header */
        HAL_TRACE_ERROR0 ("Invalid patch file header.");

        return_code = NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT;
    }

    /* If we need to download anything, get the first patch to download */
    if (nfc_hal_cb.prm.spd_patch_needed_mask)
    {
        HAL_TRACE_ERROR4 ("Downloading patch version: %i.%i (previous version in NVM: %i.%i)...",
                            patchfile_ver_major, patchfile_ver_minor,
                            nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor);
#if (defined (NFC_HAL_PRE_I2C_PATCH_INCLUDED) && (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE))
        /* Check if I2C patch is needed: if                                     */
        /*      - I2C patch file was provided using HAL_NfcPrmSetI2cPatch, and        */
        /*      -   current patch in NVM has ProjectID=0, or                    */
        /*          FPM is not present or corrupted, or                         */
        /*          or patchfile is major-ver 76+                               */
        /*          or patchfile is not for B3 (always download for B4 onward)  */
        if (  (nfc_hal_cb.prm_i2c.p_patch)
            &&(  (nfc_hal_cb.nvm_cb.project_id == 0)
               ||(nfc_hal_cb.nvm_cb.fpm_size == 0)
               ||(nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_FPM_BAD)
               ||(patchfile_ver_major >= 76)
               ||(!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_BCM20791B3)) ))
        {
            HAL_TRACE_DEBUG0 ("I2C patch fix required.");
            nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED;
        }
#endif  /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */
        nfc_hal_prm_spd_check_version_continue ();
    }
    else
    {
        static BOOLEAN firstTime = TRUE;
        if (firstTime)
        {
            HAL_TRACE_ERROR2 ("NVM patch version is %d.%d",
                              nfc_hal_cb.nvm_cb.ver_major, nfc_hal_cb.nvm_cb.ver_minor);
            firstTime = FALSE;
        }
        /* Download complete */
        nfc_hal_prm_spd_handle_download_complete (return_code);
    }
}

#if (NFC_HAL_TRACE_VERBOSE == TRUE)
/*******************************************************************************
**
** Function         nfc_hal_prm_spd_status_str
**
** Description      Return status string for a given spd status code
**
** Returns          Status string
**
*******************************************************************************/
UINT8 *nfc_hal_prm_spd_status_str (UINT8 spd_status_code)
{
    char *p_str;

    switch (spd_status_code)
    {
    case NCI_STATUS_SPD_ERROR_DEST:
        p_str = "SPD_ERROR_DEST";
        break;

    case NCI_STATUS_SPD_ERROR_PROJECTID:
        p_str = "SPD_ERROR_PROJECTID";
        break;

    case NCI_STATUS_SPD_ERROR_CHIPVER:
        p_str = "SPD_ERROR_CHIPVER";
        break;

    case NCI_STATUS_SPD_ERROR_MAJORVER:
        p_str = "SPD_ERROR_MAJORVER";
        break;

    case NCI_STATUS_SPD_ERROR_INVALID_PARAM:
        p_str = "SPD_ERROR_INVALID_PARAM";
        break;

    case NCI_STATUS_SPD_ERROR_INVALID_SIG:
        p_str = "SPD_ERROR_INVALID_SIG";
        break;

    case NCI_STATUS_SPD_ERROR_NVM_CORRUPTED:
        p_str = "SPD_ERROR_NVM_CORRUPTED";
        break;

    case NCI_STATUS_SPD_ERROR_PWR_MODE:
        p_str = "SPD_ERROR_PWR_MODE";
        break;

    case NCI_STATUS_SPD_ERROR_MSG_LEN:
        p_str = "SPD_ERROR_MSG_LEN";
        break;

    case NCI_STATUS_SPD_ERROR_PATCHSIZE:
        p_str = "SPD_ERROR_PATCHSIZE";
        break;

    default:
        p_str = "Unspecified Error";
        break;

    }

    return ((UINT8*) p_str);
}
#endif  /* (NFC_HAL_TRACE_VERBOSE == TRUE) */
/*******************************************************************************
**
** Function         nfc_hal_prm_nvm_rw_cmd
**
** Description      Non Volatile Read Write Command; for now only write zeros
**
** Returns          TRUE if done.
**
*******************************************************************************/
static BOOLEAN nfc_hal_prm_nvm_rw_cmd(void)
{
    tNFC_HAL_PRM_RM_RF  *p_param = (tNFC_HAL_PRM_RM_RF *)(nfc_hal_cb.prm.p_param);
    UINT8   *p_buff, *p, *p_end;
    UINT8   len = 0;
    UINT16  cmd_len;

    if (p_param)
        len     = p_param[nfc_hal_cb.prm.param_idx].len;
    HAL_TRACE_DEBUG2 ("nfc_hal_prm_nvm_rw_cmd: %d/%d", nfc_hal_cb.prm.param_idx, len);
    if (len == 0)
    {
        return TRUE;
    }
    cmd_len = len + 7;

    if ((p_buff = (UINT8 *) GKI_getbuf(cmd_len)) == NULL)
    {
        HAL_TRACE_ERROR0 ("NVM No buffer");
        nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_EVT);
        return TRUE;
    }

    p = p_buff;

    UINT8_TO_STREAM  (p, (NCI_MTS_CMD|NCI_GID_PROP));
    UINT8_TO_STREAM  (p, NCI_MSG_EEPROM_RW);
    UINT8_TO_STREAM  (p, (len+4));
    UINT8_TO_STREAM  (p, 1); /* 1=write 0=read */
    UINT16_TO_STREAM  (p, p_param[nfc_hal_cb.prm.param_idx].offset);
    UINT8_TO_STREAM  (p, len);
    memset (p, 0, len); /* Fill remaining bytes with zeros*/

    nfc_hal_cb.prm.param_idx++;
    nfc_hal_dm_send_nci_cmd(p_buff, cmd_len, nfc_hal_prm_nci_command_complete_cback);
    GKI_freebuf (p_buff);
    return FALSE;
}

/*******************************************************************************
**
** Function         nfc_hal_prm_nci_command_complete_cback
**
** Description      Callback for NCI vendor specific command complete
**                  (for secure patch download)
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_nci_command_complete_cback (tNFC_HAL_NCI_EVT event, UINT16 data_len, UINT8 *p_data)
{
    UINT8 status, u8;
    UINT8 *p;
    UINT32 post_signature_delay;

    NFC_HAL_PRM_STATE ("nfc_hal_prm_nci_command_complete_cback");

    /* Stop the command-timeout timer */
    nfc_hal_main_stop_quick_timer (&nfc_hal_cb.prm.timer);

    /* Skip over NCI header */
    p = p_data + NCI_MSG_HDR_SIZE;

    /* Handle SECURE_PATCH_DOWNLOAD Rsp */
    if (event == NFC_VS_SEC_PATCH_DOWNLOAD_EVT)
    {
        /* Status and error code */
        STREAM_TO_UINT8 (status, p);
        STREAM_TO_UINT8 (u8, p);

        if (status != NCI_STATUS_OK)
        {
#if (NFC_HAL_TRACE_VERBOSE == TRUE)
            HAL_TRACE_ERROR2 ("Patch download failed, reason code=0x%X (%s)", status, nfc_hal_prm_spd_status_str (status));
#else
            HAL_TRACE_ERROR1 ("Patch download failed, reason code=0x%X", status);
#endif

            /* Notify application */
            nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT);
            return;
        }

        /* If last segment (SIGNATURE) sent */
        if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_SIGNATURE_SENT)
        {
            /* Wait for authentication complete (SECURE_PATCH_DOWNLOAD NTF), including time to commit to NVM (for BCM43341B0) */
            nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_AUTHENTICATING;
            nfc_hal_main_start_quick_timer (&nfc_hal_cb.prm.timer, 0x00,
                                            (NFC_HAL_PRM_COMMIT_DELAY * QUICK_TIMER_TICKS_PER_SEC) / 1000);
            return;
        }
        /* Download next segment */
        else if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF)
        {
            /* If patch is in a buffer, get next patch from buffer */
            nfc_hal_prm_spd_send_next_segment ();
        }
        else
        {
            /* Notify adaptation layer to get next patch segment (via HAL_NfcPrmDownloadContinue) */
            (nfc_hal_cb.prm.p_cback) (NFC_HAL_PRM_CONTINUE_EVT);
        }
    }
    /* Handle SECURE_PATCH_DOWNLOAD NTF */
    else if (event == NFC_VS_SEC_PATCH_AUTH_EVT)
    {
        HAL_TRACE_DEBUG1 ("prm flags:0x%x.", nfc_hal_cb.prm.flags);
        /* Status and error code */
        STREAM_TO_UINT8 (status, p);
        STREAM_TO_UINT8 (u8, p);

        /* Sanity check - should only get this NTF while in AUTHENTICATING stage */
        if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTHENTICATING)
        {
            if (status != NCI_STATUS_OK)
            {
                HAL_TRACE_ERROR0 ("Patch authentication failed");
                nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_BAD_SIGNATURE_EVT);
                return;
            }

#if (defined (NFC_HAL_PRE_I2C_PATCH_INCLUDED) && (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE))
            if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED)
            {
                HAL_TRACE_DEBUG1 ("PreI2C patch downloaded...waiting %i ms for NFCC to reboot.", nfc_hal_cb.prm_i2c.prei2c_delay);

                /* Restore pointers to patchfile */
                nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_I2C_FIX_REQUIRED;
                nfc_hal_cb.prm.p_cur_patch_data = nfc_hal_cb.prm.p_spd_patch;
                nfc_hal_cb.prm.cur_patch_offset = nfc_hal_cb.prm.spd_patch_offset;
                nfc_hal_cb.prm.cur_patch_len_remaining = nfc_hal_cb.prm.spd_patch_len_remaining;

                /* Resume normal patch download */
                nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER;
                nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_SIGNATURE_SENT;

                /* Post PreI2C delay */
                nfc_hal_main_start_quick_timer (&nfc_hal_cb.prm.timer, 0x00, (nfc_hal_cb.prm_i2c.prei2c_delay * QUICK_TIMER_TICKS_PER_SEC) / 1000);

                return;
            }
#endif  /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */


            /* Wait for NFCC to save the patch to NVM */
            if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_BCM20791B3))
            {
                /* 20791B4 or newer - wait for RESET_NTF; including time to commit to NVM (for BCM20791B4+) */
                post_signature_delay = NFC_HAL_PRM_COMMIT_DELAY;
                HAL_TRACE_DEBUG1 ("Patch downloaded and authenticated. Waiting %i ms for RESET NTF...", post_signature_delay);

            }
            else if (nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_NO_NVM)
            {
                /* No NVM. Wait for NFCC to restart */
                post_signature_delay = NFC_HAL_PRM_END_DELAY;
                HAL_TRACE_DEBUG1 ("Patch downloaded and authenticated. Waiting %i ms for NFCC to restart...", post_signature_delay);
            }
            else
            {
                /* Wait for NFCC to save the patch to NVM (need about 1 ms per byte) */
                post_signature_delay = nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].len;
                if (post_signature_delay < nfc_hal_cb.prm.patchram_delay)
                    post_signature_delay = nfc_hal_cb.prm.patchram_delay;
                HAL_TRACE_DEBUG1 ("Patch downloaded and authenticated. Waiting %i ms for NVM update to complete...", post_signature_delay);
            }

            nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_AUTH_DONE;

            nfc_hal_main_start_quick_timer (&nfc_hal_cb.prm.timer, 0x00,
                                            (post_signature_delay * QUICK_TIMER_TICKS_PER_SEC) / 1000);
        }
        else
        {
            HAL_TRACE_ERROR0 ("Got unexpected SECURE_PATCH_DOWNLOAD NTF");
            nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_EVT);
        }
    }
    /* Handle NCI_MSG_GET_PATCH_VERSION RSP */
    else if (event == NFC_VS_GET_PATCH_VERSION_EVT)
    {
        nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_COMPLETE_EVT);
    }
    else if (event == NFC_VS_EEPROM_RW_EVT)
    {
        STREAM_TO_UINT8 (status, p);
        if (status == NCI_STATUS_OK)
        {
            if (nfc_hal_prm_nvm_rw_cmd ())
            {
                nfc_hal_prm_spd_check_version_continue ();
            }
        }
        else
        {
            HAL_TRACE_ERROR0 ("NVM failed");
            nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_EVT);
        }
    }
    else
    {
        /* Invalid response from NFCC during patch download */
        HAL_TRACE_ERROR1 ("Invalid response from NFCC during patch download (opcode=0x%02X)", event);
        nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_INVALID_PATCH_EVT);
    }

    NFC_HAL_PRM_STATE ("prm_nci_command_complete_cback");
}

/*******************************************************************************
**
** Function         nfc_hal_prm_nfcc_ready_to_continue
**
** Description      Continue to download patch or notify application completition
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_nfcc_ready_to_continue (void)
{
    UINT8 get_patch_version_cmd [NCI_MSG_HDR_SIZE] =
    {
        NCI_MTS_CMD|NCI_GID_PROP,
        NCI_MSG_GET_PATCH_VERSION,
        0x00
    };

    /* Clear the bit for the patch we just downloaded */
    nfc_hal_cb.prm.spd_patch_needed_mask &= ~ ((UINT32) 1 << nfc_hal_cb.prm.spd_patch_desc[nfc_hal_cb.prm.spd_cur_patch_idx].power_mode);

    /* Check if another patch to download */
    nfc_hal_cb.prm.spd_cur_patch_idx++;
    if ((nfc_hal_cb.prm.spd_patch_needed_mask) && (nfc_hal_cb.prm.spd_cur_patch_idx < nfc_hal_cb.prm.spd_patch_count))
    {
        nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER;
        nfc_hal_cb.prm.flags &= ~NFC_HAL_PRM_FLAGS_SIGNATURE_SENT;

        if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF)
        {
            /* If patch is in a buffer, get next patch from buffer */
            nfc_hal_prm_spd_handle_next_patch_start ();
        }
        else
        {
            /* Notify adaptation layer to get next patch header (via HAL_NfcPrmDownloadContinue) */
            (nfc_hal_cb.prm.p_cback) (NFC_HAL_PRM_SPD_GET_NEXT_PATCH);
        }

    }
    else
    {
        /* Done downloading */
        HAL_TRACE_DEBUG0 ("Patch downloaded and authenticated. Get new patch version.");
        /* add get patch info again to verify the effective FW version */
        nfc_hal_dm_send_nci_cmd (get_patch_version_cmd, NCI_MSG_HDR_SIZE, nfc_hal_prm_nci_command_complete_cback);
        nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_W4_GET_VERSION;
    }
}

/*******************************************************************************
**
** Function         nfc_hal_prm_spd_reset_ntf
**
** Description      Received RESET NTF from NFCC, indicating it has completed
**                  reset after patch download.
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_spd_reset_ntf (UINT8 reset_reason, UINT8 reset_type)
{
    /* Check if we were expecting a RESET NTF */
    if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTH_DONE)
    {
        HAL_TRACE_DEBUG2 ("Received RESET NTF after patch download (reset_reason=%i, reset_type=%i)", reset_reason, reset_type);

        /* Stop waiting for RESET NTF */
        nfc_hal_main_stop_quick_timer (&nfc_hal_cb.prm.timer);

        {
            /* Continue with patch download */
            nfc_hal_prm_nfcc_ready_to_continue ();
        }
    }
    else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER)
    {
        HAL_TRACE_DEBUG0 ("Received RESET NTF after pre-I2C patch download. Proceeding with patch download...");

        /* Stop waiting for RESET NTF */
        nfc_hal_main_stop_quick_timer (&nfc_hal_cb.prm.timer);
        nfc_hal_prm_spd_handle_next_patch_start ();
    }
    else
    {
        HAL_TRACE_ERROR2 ("Received unexpected RESET NTF (reset_reason=%i, reset_type=%i)", reset_reason, reset_type);
    }
}

/*******************************************************************************
**
** Function:    nfc_post_final_baud_update
**
** Description: Called after baud rate udate
**
** Returns:     Nothing
**
*******************************************************************************/
void nfc_hal_prm_post_baud_update (tHAL_NFC_STATUS status)
{
    NFC_HAL_PRM_STATE ("nfc_hal_prm_post_baud_update");

    if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTH_DONE)
    {
        /* Proceed with next step of patch download sequence */
        nfc_hal_prm_nfcc_ready_to_continue ();
    }
}

/*******************************************************************************
**
** Function         nfc_hal_prm_process_timeout
**
** Description      Process timer expireation for patch download
**
** Returns          void
**
*******************************************************************************/
void nfc_hal_prm_process_timeout (void *p_tle)
{
    NFC_HAL_PRM_STATE ("nfc_hal_prm_process_timeout");

    if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_AUTH_DONE)
    {
        if (!(nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_BCM20791B3))
        {
            /* Timeout waiting for RESET NTF after signature sent */
            HAL_TRACE_ERROR0 ("Timeout waiting for RESET NTF after patch download");
            nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_EVT);
        }
        else
        {
            nfc_hal_prm_nfcc_ready_to_continue ();
        }
    }
    else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER)
    {
        HAL_TRACE_DEBUG0 ("Delay after PreI2C patch download...proceeding to download firmware patch");
        nfc_hal_prm_spd_handle_next_patch_start ();
    }
    else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_W4_GET_VERSION)
    {
        HAL_TRACE_DEBUG0 ("get patch version timeout???");
        nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_COMPLETE_EVT);
    }
    else
    {
        HAL_TRACE_ERROR1 ("Patch download: command timeout (state=%i)", nfc_hal_cb.prm.state);

        nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_EVT);
    }

    NFC_HAL_PRM_STATE ("nfc_hal_prm_process_timeout");
}


/*******************************************************************************
**
** Function         HAL_NfcPrmDownloadStart
**
** Description      Initiate patch download
**
** Input Params
**                  format_type     patch format type
**                                  (NFC_HAL_PRM_FORMAT_BIN, NFC_HAL_PRM_FORMAT_HCD, or
**                                   NFC_HAL_PRM_FORMAT_NCD)
**
**                  dest_address    destination adderess (needed for BIN format only)
**
**                  p_patchram_buf  pointer to patchram buffer. If NULL,
**                                  then app must call HAL_NfcPrmDownloadContinue when
**                                  NFC_HAL_PRM_CONTINUE_EVT is received, to send the next
**                                  segment of patchram
**
**                  patchram_len    size of p_patchram_buf (if non-NULL)
**
**                  patchram_delay  The delay after each patch.
**                                  If the given value is less than the size of the patchram,
**                                  the size of patchram is used instead.
**
**                  p_cback         callback for download status
**
**
** Returns          TRUE if successful, otherwise FALSE
**
**
*******************************************************************************/
BOOLEAN HAL_NfcPrmDownloadStart (tNFC_HAL_PRM_FORMAT format_type,
                                 UINT32              dest_address,
                                 UINT8               *p_patchram_buf,
                                 UINT32              patchram_len,
                                 UINT32              patchram_delay,
                                 tNFC_HAL_PRM_CBACK  *p_cback)
{
    HAL_TRACE_API0 ("HAL_NfcPrmDownloadStart ()");

    memset (&nfc_hal_cb.prm, 0, sizeof (tNFC_HAL_PRM_CB));

    if (p_patchram_buf)
    {
        nfc_hal_cb.prm.p_cur_patch_data = p_patchram_buf;
        nfc_hal_cb.prm.cur_patch_offset = 0;
        nfc_hal_cb.prm.cur_patch_len_remaining = (UINT16) patchram_len;
        nfc_hal_cb.prm.flags |= NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF;

        if (patchram_len == 0)
            return FALSE;
    }

    nfc_hal_cb.prm.p_cback          = p_cback;
    nfc_hal_cb.prm.dest_ram         = dest_address;
    nfc_hal_cb.prm.format           = format_type;
    nfc_hal_cb.prm.patchram_delay   = patchram_delay;

    nfc_hal_cb.prm.timer.p_cback = nfc_hal_prm_process_timeout;

    if (format_type == NFC_HAL_PRM_FORMAT_NCD)
    {
        /* Store patch buffer pointer and length */
        nfc_hal_cb.prm.p_spd_patch             = p_patchram_buf;
        nfc_hal_cb.prm.spd_patch_len_remaining = (UINT16)patchram_len;
        nfc_hal_cb.prm.spd_patch_offset        = 0;

        /* If patch download is required, but no NVM is available, then abort */
        if ((p_nfc_hal_cfg->nfc_hal_prm_nvm_required) && (nfc_hal_cb.nvm_cb.flags & NFC_HAL_NVM_FLAGS_NO_NVM))
        {
            HAL_TRACE_ERROR0 ("This platform requires NVM and the NVM is not available - Abort");
            nfc_hal_prm_spd_handle_download_complete (NFC_HAL_PRM_ABORT_NO_NVM_EVT);
            return FALSE;
        }

        /* Compare patch version in NVM with version in patchfile */
        nfc_hal_cb.prm.state = NFC_HAL_PRM_ST_SPD_COMPARE_VERSION;
        if (nfc_hal_cb.prm.flags & NFC_HAL_PRM_FLAGS_USE_PATCHRAM_BUF)
        {
            /* If patchfile is in a buffer, get patch version from buffer */
            nfc_hal_prm_spd_check_version ();
        }
        else
        {
            /* If patchfile is not in a buffer, then request patchfile header from adaptation layer. */
            (nfc_hal_cb.prm.p_cback) (NFC_HAL_PRM_SPD_GET_PATCHFILE_HDR_EVT);
        }
    }
    else
    {
        HAL_TRACE_ERROR0 ("Unexpected patch format.");
        return FALSE;
    }

    return TRUE;
}

/*******************************************************************************
**
** Function         HAL_NfcPrmDownloadContinue
**
** Description      Send next segment of patchram to controller. Called when
**                  NFC_HAL_PRM_CONTINUE_EVT is received.
**
**                  Only needed if HAL_NfcPrmDownloadStart was called with
**                  p_patchram_buf=NULL
**
** Input Params     p_patch_data    pointer to patch data
**                  patch_data_len  patch data len
**
** Returns          TRUE if successful, otherwise FALSE
**
*******************************************************************************/
BOOLEAN HAL_NfcPrmDownloadContinue (UINT8 *p_patch_data,
                                    UINT16 patch_data_len)
{
    HAL_TRACE_API2 ("HAL_NfcPrmDownloadContinue ():state = %d, patch_data_len=%d",
                     nfc_hal_cb.prm.state, patch_data_len);

    /* Check if we are in a valid state for this API */
    if (  (nfc_hal_cb.prm.state != NFC_HAL_PRM_ST_SPD_COMPARE_VERSION)
        &&(nfc_hal_cb.prm.state != NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER)
        &&(nfc_hal_cb.prm.state != NFC_HAL_PRM_ST_SPD_DOWNLOADING)  )
        return FALSE;

    if (patch_data_len == 0)
        return FALSE;

    nfc_hal_cb.prm.cur_patch_offset = 0;
    nfc_hal_cb.prm.p_cur_patch_data = p_patch_data;
    nfc_hal_cb.prm.cur_patch_len_remaining = patch_data_len;

    /* Call appropriate handler */
    if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_COMPARE_VERSION)
    {
        nfc_hal_prm_spd_check_version ();
    }
    else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_GET_PATCH_HEADER)
    {
        nfc_hal_prm_spd_handle_next_patch_start ();
    }
    else if (nfc_hal_cb.prm.state == NFC_HAL_PRM_ST_SPD_DOWNLOADING)
    {
        nfc_hal_prm_spd_send_next_segment ();
    }
    else
    {
        HAL_TRACE_ERROR1 ("Unexpected patch state:%d.", nfc_hal_cb.prm.state);
    }

    return TRUE;
}

/*******************************************************************************
**
** Function         HAL_NfcPrmSetI2cPatch
**
** Description      Specify patchfile for BCM20791B3 I2C fix. This fix
**                  must be downloaded prior to initial patch download for I2C
**                  transport
**
** Input Params     p_i2c_patchfile_buf: pointer to patch for i2c fix
**                  i2c_patchfile_len: length of patch
**                  prei2c_delay: the delay before downloading main patch
**                                if 0 is given, NFC_HAL_PRM_POST_I2C_FIX_DELAY is used instead.
**
** Returns          Nothing
**
**
*******************************************************************************/
void HAL_NfcPrmSetI2cPatch (UINT8 *p_i2c_patchfile_buf, UINT16 i2c_patchfile_len, UINT32 prei2c_delay)
{
#if (defined (NFC_HAL_PRE_I2C_PATCH_INCLUDED) && (NFC_HAL_PRE_I2C_PATCH_INCLUDED == TRUE))
    HAL_TRACE_API0 ("HAL_NfcPrmSetI2cPatch ()");

    nfc_hal_cb.prm_i2c.prei2c_delay    = NFC_HAL_PRM_POST_I2C_FIX_DELAY;
    if (prei2c_delay)
        nfc_hal_cb.prm_i2c.prei2c_delay = prei2c_delay;
    nfc_hal_cb.prm_i2c.p_patch = p_i2c_patchfile_buf;
    nfc_hal_cb.prm_i2c.len = i2c_patchfile_len;
#endif  /* NFC_HAL_PRE_I2C_PATCH_INCLUDED */
}

/*******************************************************************************
**
** Function         HAL_NfcPrmSetSpdNciCmdPayloadSize
**
** Description      Set Host-to-NFCC NCI message size for secure patch download
**
**                  This API must be called before calling HAL_NfcPrmDownloadStart.
**                  If the API is not called, then PRM will use the default
**                  message size.
**
**                  Typically, this API is only called for platforms that have
**                  message-size limitations in the transport/driver.
**
**                  Valid message size range: NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE to 255.
**
** Returns          HAL_NFC_STATUS_OK if successful
**                  HAL_NFC_STATUS_FAILED otherwise
**
**
*******************************************************************************/
tHAL_NFC_STATUS HAL_NfcPrmSetSpdNciCmdPayloadSize (UINT8 max_payload_size)
{
    /* Validate: minimum size is NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE */
    if (max_payload_size < NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE)
    {
        HAL_TRACE_ERROR2 ("HAL_NfcPrmSetSpdNciCmdPayloadSize: invalid size (%i). Must be between %i and 255", max_payload_size, NFC_HAL_PRM_MIN_NCI_CMD_PAYLOAD_SIZE);
        return (HAL_NFC_STATUS_FAILED);
    }
    else
    {
        HAL_TRACE_API1 ("HAL_NfcPrmSetSpdNciCmdPayloadSize: new message size during download: %i", max_payload_size);
        nfc_hal_cb.ncit_cb.nci_ctrl_size = max_payload_size;
        return (HAL_NFC_STATUS_OK);
    }
}
